/**
 * Beinhaltet einige Funktionen zum Zugriff auf das LCD Modell WD2704M
 * von Wintek mit zwei HD44780 Kontrollern und 4x27 Zeichen Displayzeilen.
 *
 * (c) Copyright 2007 von Enrico Homann
 */

#include "lcd_wd2704m.h"


/**
 * void lcd_wd2704m_gotoxy ( uint8_t x, uint8_t y )
 **
 * Setzt den DDRAM des zustaendigen Kontrollers entsprechend den x und 
 * y Koordinaten. Liefert den zustaendigen Kontroller zurueck
 **
 * @param x uint8_t x Koordinate
 * @param y uint8_t y Koordinate
 */

uint8_t lcd_wd2704m_gotoxy ( uint8_t x, uint8_t y ) {

	uint8_t E;

	if (y <= LCD_WD2704M_MAX_X ) lcd_hd44780_gotoxy( E=LCD_HD44780_E1, x, y ); 
          else lcd_hd44780_gotoxy( E=LCD_HD44780_E2, x, ((y >= 3) ? 1 : 0) );

	return E;								/* Aktiven Kontroller zurueck liefern */
}

/**
 * void lcd_wd2704m_writestrxy ( uint8_t x, uint8_t y, unsigned char *str )
 **
 * Schreibt ein String str an die mit x,y angegebenen Koordinaten
 **
 * @param x uint8_t x Koordinate
 * @param y uint8_t y Koordinate
 * @param str *char String 
 */

void lcd_wd2704m_writestrxy ( uint8_t x, uint8_t y, char *str ) {

	lcd_hd44780_writestr ( lcd_wd2704m_gotoxy(x,y), str );

}

/**
 * void lcd_wd2704m_writestrxy ( uint8_t x, uint8_t y, unsigned char str )
 **
 * Schreibt ein Zeichen str an die mit x,y angegebenen Koordinaten
 **
 * @param x uint8_t x Koordinate
 * @param y uint8_t y Koordinate
 * @param str unsigned char Zeichen 
 */

void lcd_wd2704m_writechrxy ( uint8_t x, uint8_t y, unsigned char str ) {

	lcd_hd44780_writechar ( lcd_wd2704m_gotoxy(x,y), str );

}

/**
 * void lcd_wd2704m_writechar ( unsigned char str )
 **
 * Schreibt ein Zeichen str an die aktuelle per lcd_wd2704m_curosr_pos
 * angebene Position. (virtuelle DDRAM Position von den zwei Kontrollern)
 **
 * @param str unsigned char Zeichen 
 */

void lcd_wd2704m_writechar ( unsigned char chr ) {

	lcd_wd2704m_writechrxy ( lcd_wd2704m_cursor_pos_s.x, lcd_wd2704m_cursor_pos_s.y, chr );

	/* "Rotierende" Koordinaten, sobald x und y = MAX => x = 0, y = 0 */
	if (lcd_wd2704m_cursor_pos_s.x >= LCD_WD2704M_MAX_X) { 		/* Naechste Zeile */
		lcd_wd2704m_cursor_pos_s.x = 0; 

		if (lcd_wd2704m_cursor_pos_s.y >= LCD_WD2704M_MAX_Y) {
			lcd_wd2704m_cursor_pos_s.y = 0; 
		} else lcd_wd2704m_cursor_pos_s.y++;

	} else lcd_wd2704m_cursor_pos_s.x++;


}

/**
 * void lcd_wd2704m_writeframe27x4_f ( const lcd27x4frame_t frame )
 **
 * Schreibt ein Arrayfeld (27x4) in den LCD DDRAM.
 * Liest aus den Flashspeicher (PROGMEM)!
 **
 * @param lcd27x4frame_t frame Arrayfeld
 */

void lcd_wd2704m_writeframe27x4_f ( const lcd27x4frame_t frame  ) {

	uint8_t y; char *buf;

	buf = malloc(28);

	for (y = 0; y <= 3; y++) {
		strlcpy_P ( (char*) buf, (char*) frame[y], 27);
		buf[27] = '\0';						// Endstringtag setzen
		lcd_wd2704m_writestrxy(0, y, buf);
	};

	free(buf);
}

/**
 * void lcd_wd2704m_cleardisplay ()
 **
 * Loescht den DDRAM beider Kontroller
 **
 * @param lcd27x4frame_t frame Arrayfeld
 */

void lcd_wd2704m_cleardisplay () {

	lcd_hd44780_command ( LCD_HD44780_E1, LCD_HD44780_COMMAND_CLEARDISPLAY );
	lcd_hd44780_command ( LCD_HD44780_E2, LCD_HD44780_COMMAND_CLEARDISPLAY );

	lcd_wd2704m_cursor_pos_s. x = 0;
	lcd_wd2704m_cursor_pos_s. y = 0;

}

/**
 * void lcd_wd2704m_command ( uint8_t command )
 **
 * Gibt command als Intruktion an beide Kontroller aus.
 **
 * @param uint8_t command Instruktion
 */

void lcd_wd2704m_command ( uint8_t command ) {

	lcd_hd44780_command ( LCD_HD44780_E1, command );
	lcd_hd44780_command ( LCD_HD44780_E2, command );

}

/**
 * void lcd_wd2704m_init ( uint8_t fs, uint8_t es, uint8_t ds )
 **
 * Initialisiert beide Kontroller mit 
 * fs = Functionset, es = Entrymodeset, ds = Displayset
 * und initialisiert die virtuelle DDRAM Position 
 **
 * @param uint8_t fs Functionset
 * @param uint8_t es Entrymodeset
 * @param uint8_t ds Displayset
 */

void lcd_wd2704m_init ( uint8_t fs, uint8_t es, uint8_t ds ) {

	/* Kontroller 1 initialisieren (oberer) */
	lcd_hd44780_init ( LCD_HD44780_E1, fs, es, ds );		
	/* Kontroller 2 initialisieren (unterer) */
	lcd_hd44780_init ( LCD_HD44780_E2, fs, es, ds );

	/* Interne Cursorpos (fuer lcd_wd2704m_writechar()) initialisieren */
	lcd_wd2704m_cursor_pos_s.x = 0;
	lcd_wd2704m_cursor_pos_s.y = 0;


}

/**
 * void lcd_wd2704m_gfx_shiftdisplay_cross ( bool e1_right )
 **
 * Spezialgrafik Routine: Laesst den DDRAM beider Kontroller entgegengesetzt
 * nach links bzw rechts verschieben.
 * e1_right = true  -> Kontroller 1 rechts; Kontroller 2 links
 * e1_right = false -> Kontroller 1 links ; Kontroller 2 rechts
 **
 * @param bool e1_right siehe oben
 */

void lcd_wd2704m_gfx_shiftdisplay_cross ( bool e1_right ) {

	if (e1_right) {
        	lcd_hd44780_command ( LCD_HD44780_E1, LCD_HD44780_COMMAND_SHIFT_DISPLAY_RIGHT );
	        lcd_hd44780_command ( LCD_HD44780_E2, LCD_HD44780_COMMAND_SHIFT_DISPLAY_LEFT );
	} else {
        	lcd_hd44780_command ( LCD_HD44780_E1, LCD_HD44780_COMMAND_SHIFT_DISPLAY_LEFT );
	        lcd_hd44780_command ( LCD_HD44780_E2, LCD_HD44780_COMMAND_SHIFT_DISPLAY_RIGHT );
	}


}

/**
 * void lcd_wd2704m_playmovie27x4_f ( const lcd27x4frame_t movie[], uint8_t frames )
 **
 * Spezialgrafik Routine: Schreibt ein Arrayfeld (27x4) in den LCD DDRAM.
 * Liest aus den Flashspeicher (PROGMEM)!
 **
 * @param movie27x_t m Zeiger auf Filmdaten 
 */

void lcd_wd2704m_gfx_playmovie27x4_f ( const movie27x4_t *m ) {

	uint8_t y, frame; char *buf;
	movie27x4_t movie_buf;
	
	strlcpy_P ( (char*) movie_buf.name, (char*) (PGM_P) &m->name, sizeof(movie_buf.name));
	movie_buf.frames = pgm_read_byte_near ( &m->frames);
	movie_buf.pause_ms = pgm_read_byte_near ( &m->pause_ms);
	movie_buf.ms_loop = pgm_read_byte_near ( &m->ms_loop);

	buf = malloc(27);

	for (frame = 0; frame < movie_buf.frames; frame++) {
		for (y = 0; y <= 3; y++) {
			strlcpy_P ( (char*) buf, (char*) (PGM_P) &m->movie[frame][y], 27);		/* Speicher sparen und zeilenweise einlesen */
			lcd_wd2704m_writestrxy(0, y, buf);				/* ausgeben */
			
		}
		for(y=0; y < movie_buf.ms_loop; y++) _delay_ms(movie_buf.pause_ms);			/* Pause zw. Frames */
	};

	free(buf);

}

/**
 * void lcd_wd2704m_gfx_playencodedmovie_rle_f ( const rle_encodedmovie_t *m )
 **
 * Spezialgrafik Routine: Spielt den ueber *m angegebenen RLE kodierten Film
 * ab.
 **
 * @param const rle_codedmovie_t *m Zeiger auf Filmdaten
 */

void lcd_wd2704m_gfx_playencodedmovie_rle_f ( const rle_encodedmovie_t *m ) {

	uint8_t pause_ms, ms_loop, x_size, y_size;
	uint16_t encoded_size;

	pause_ms = pgm_read_byte_near ( &m->pause_ms);
	ms_loop = pgm_read_byte_near ( &m->ms_loop);
	x_size = pgm_read_byte_near ( &m->x_size );
	y_size = pgm_read_byte_near ( &m->y_size );
	encoded_size = pgm_read_word_near ( &m->encoded_size );

	uint8_t frame, x, y, j, q; uint8_t akt, w, l, buf[x_size+1];

	char *mbuf; uint8_t bufm_x;

	lcd_wd2704m_cleardisplay();

	uint16_t i, k; 

	mbuf = malloc(x_size+1);

	i = 0; x= 0; k = 0; y = 0; j = 0; frame = 0; bufm_x = 0; 

	for (i=0; i < encoded_size; i++) {

		akt = pgm_read_byte_near( &m->movie[i] ); 

		if (akt != 0xff) buf[j++] = akt; 				/* in Zeilenbuffer einlesen bis 0xff */
		else {

			x = 0; bufm_x = 0;
			while ( x < j ) {

				if ( buf[x] & (1<<RLE_SIGN) ) {			/* Kodiert */
					w = buf[x] & 0x7f;			/* MSB ausblenden */	
					l = buf[++x];				/* Anzahl Zeichen */
					for (q = 0; q < l; q++) {
						mbuf[bufm_x++] = w;
					/*	 printf("%c", w); */ 
					}
				} else {
					mbuf[bufm_x++] = buf[x]; 
				/*	 printf("%c", buf[x]); */
				}

				x++;
			}
			j = 0;
			
		/*	printf("\n"); */
				
			mbuf[bufm_x] = '\0';				// Stringabschluss
			lcd_wd2704m_writestrxy(0,y, mbuf);				/* dekodierte Zeile ausgeben */

			if (y >= y_size-1) { 					/* Ende des Frames */
				y = 0;
				for (q=0; q < 100; q++) _delay_ms( 1 );		/* Pause zwischen den Frames */
				frame++;
			

			} else y++; 						/* Naechste Zeile */

		}
	}

	free(mbuf);
	

}
